/* Trabajo Practico 4 (Primera Entrega)
# Servidor Partida
#
# - Integrantes:
#	- Martinez, Guillermo		DNI: 34510461
#	- Rodriguez, Lucas			DNI: 34265017
*/

#include "include/defines.h"
#include "include/sockets/sockets.h"
#include "include/sdl/pantalla.h"
#include "include/s_func/tateti.h"
#include "Mensajes.h"
#include "servPartida.h"

using namespace std;

// Estructura movimiento
s_mov mov;

// Socket a operar
int socket_op;

// Mensajes inicio
Mensaje msj_inicio;

// Threads IDs
pthread_t tid_socket;
pthread_t tid_pantalla;
pthread_t tid_lectura;

// Semáforos movimientos
sem_t nuevo_mov;
sem_t sem_mov;
sem_t mov_valido;

// Semáforos threads
sem_t mutex_pantalla;
sem_t mutex_periferico;
sem_t mutex_socket;
sem_t mutex_socket_s;
sem_t socket_disp;

// Nombre Jugadores
char nombre1[20];
char nombre2[20];

extern ConfiguracionServidor ConfigServer;

void CrearServidorPartida(const DatosJugador Jugador1,const DatosJugador Jugador2, int Ronda)
{
	pid_t pid;

	if ((pid = fork()) < 0)
		exit(EXIT_FAILURE);
		
	if (pid > 0)
		return;

	//Codigo Servidor de Partida
	cout << getpid() << ">> Servidor de partida para jugadores: " << Jugador1.Nombre << " "<<Jugador2.Nombre <<endl;
	
	iniciar_semaforos();
	
	// Mensajes inicio de clientes
	msj_inicio.Tipo=IniciaPartida;
	msj_inicio.Ronda=Ronda;
	
	// Inicio Thread Socket
	pthread_create(&tid_socket, NULL, thread_socket_s, (void*) &mov);
	usleep(500);
	
	// Enviar Inicio partida a cliente 1
	strcpy(msj_inicio.Nombre,Jugador2.Nombre);
	if(Jugador1.Conectado && !Jugador1.Fantasma)
	{	
		cout << getpid() << ">> Se envia mensaje a jugador 1" << endl;  
		unlock_socket_s(INICIO,Jugador1.Descriptor);
	}
	sem_wait(&nuevo_mov);
	
	// Enviar Inicio partida a cliente 2
	strcpy(msj_inicio.Nombre,Jugador1.Nombre);
	if(Jugador2.Conectado && !Jugador2.Fantasma)
	{
		cout << getpid() << ">> Se envia mensaje a jugador 2" << endl;
		unlock_socket_s(INICIO,Jugador2.Descriptor);
	}
	sem_wait(&nuevo_mov);

	/////////Logica partida
	
	EstadoPartida Partida;
	Partida.Jugador1=Jugador1;
	Partida.Jugador2=Jugador2;
	Partida.PuntajeJug1=0;
	Partida.PuntajeJug2=0;
	Partida.IdServidorPartida=getpid();
	
	srand(getpid()); //Temporal
	
	bool GanaJug1;
	////////////////////////////////////////////////////////////////////////////
	
	while(Partida.PuntajeJug1+Partida.PuntajeJug2 < ConfigServer.CantJuegosPorPartida)
	{
		if(Jugador1.Fantasma || Jugador2.Fantasma || !Jugador1.Conectado || !Jugador2.Conectado)
		{
			//Cantidad impar o desconexion
			if( Jugador2.Fantasma || !Jugador2.Conectado )
			{
				GanaJug1 = true;
				Partida.PuntajeJug1 += Partida.PuntajeJug2;
				Partida.PuntajeJug2 = 0;
				cout << getpid() << ">> Se da por ganador a " << Jugador1.Nombre << endl;
			}
			
			else
			{
				GanaJug1 = false;
				Partida.PuntajeJug2 += Partida.PuntajeJug1;
				Partida.PuntajeJug1 = 0;
				cout << getpid() << ">> Se da por ganador a " << Jugador2.Nombre << endl;
			}
		}
		
		else
			GanaJug1 = JugarTateti(Jugador1, Jugador2);
		
		if(GanaJug1)
			Partida.PuntajeJug1++;
		else
			Partida.PuntajeJug2++;
		
		// Si no ganó la última partida
		if(Partida.PuntajeJug1+Partida.PuntajeJug2 < ConfigServer.CantJuegosPorPartida)
		{
			// Confirmo ganador y perdedor de partida
			sem_wait(&sem_mov);
			mov.estado = GANADOR;
			mov.accion = INVALIDO;
			mov.n_posicion = mov.posicion = 0;
		
			if(GanaJug1)
			{
				//Partida.PuntajeJug1++;
				unlock_socket_s(CONF,Jugador1.Descriptor);
				sem_wait(&sem_mov);
				mov.estado = PERDEDOR;
				unlock_socket_s(CONF,Jugador2.Descriptor);
			}
		
			else
			{
				//Partida.PuntajeJug2++;
				unlock_socket_s(CONF,Jugador2.Descriptor);
				sem_wait(&sem_mov);
				mov.estado = PERDEDOR;
				unlock_socket_s(CONF,Jugador1.Descriptor);
			}
		}
		
		cout << getpid() << ">> " << Jugador1.Nombre << " " << Partida.PuntajeJug1
		                 << " - " << Jugador2.Nombre << " " << Partida.PuntajeJug2 <<endl;
		
		EscribirMemoriaCompartida(Partida);
		sleep(1);
	}
	
	// Terminó la ronda para estos dos jugadores
	cout << getpid() << " Finaliza servidor partida" << endl;
	int aux=-22;
	sem_getvalue(&sem_mov,&aux);
	printf("sem_mov: %d |",aux);
	sem_getvalue(&nuevo_mov,&aux);
	printf("nuevo_mov: %d |",aux);
	sem_getvalue(&socket_disp,&aux);
	printf("socket_disp: %d\n",aux);
	
	// Envío mensajes de término de ronda a ganador
	sem_wait(&sem_mov);
	mov.estado = GANADOR_RONDA;
	mov.accion = mov.posicion = mov.n_posicion = 0;
	
	if(GanaJug1)
	{
		unlock_socket_s(CONF,Jugador1.Descriptor);
		sem_wait(&sem_mov);
		printf("Enviado a 1 ganador partida\n");
		
		// Envío mensajes de término de ronda a perdedor
		mov.estado = PERDEDOR_RONDA;
		unlock_socket_s(CONF,Jugador2.Descriptor);
		printf("Enviado a 2 perdedor partida\n");
	}
	
	else
	{
		unlock_socket_s(CONF,Jugador2.Descriptor);
		sem_wait(&sem_mov);
		printf("Enviado a 2 ganador partida\n");
		
		// Envío mensajes de término de ronda a perdedor
		mov.estado = PERDEDOR_RONDA;
		unlock_socket_s(CONF,Jugador1.Descriptor);
		printf("Enviado a 1 perdedor partida\n");
	}
	
	// Espero que haya terminado envío
	sem_wait(&socket_disp);
	
	// Finalizar sockets
	pthread_kill(tid_socket,SIGINT);
	
	// Espero a que terminen
	pthread_join(tid_socket, NULL);
	
	exit(0); //Finaliza la partida -- 
}

//Devuelve si gano el Jugador 1
bool JugarTateti(DatosJugador Jugador1, DatosJugador Jugador2)
{
	//Logica de Juego comunicacion con cliente (para cada jueguito del tateti)
	// MENSAJES AL CLIENTE - FALTA INTEGRAR
	
	printf("A jugar %s y %s\n",Jugador1.Nombre,Jugador2.Nombre);
	
	int victoriasJ1, victoriasJ2, ganador;
	int turno, fichas[2], tablero[TABLERO][TABLERO];
	int fOrigen, cOrigen, fDestino, cDestino, pos;		// [0]=Servidor | [1]=Jugador1 | [2]=Jugador2
	short int ult_mov = 0;								// Registra último mov
	int value=-1,sock_jugador;
	
	fichas[0] = fichas[1] = 0;
	InicializarTablero(tablero);

	sem_wait(&sem_mov);
	mov.estado = NO_TERMINO;					// No hay ganador todavía
	mov.accion = INVALIDO;						// Si tiene que repetir, por inválida
	mov.posicion = ult_mov = 0;
	sem_post(&sem_mov);

	do
	{
		SeleccionaTurno(&turno);
		if(turno == FICHA_O)
			sock_jugador = Jugador1.Descriptor;
		else
			sock_jugador = Jugador2.Descriptor;

		if(fichas[turno-1] < 3)				// Proceso una colocación de ficha
		{
			do
			{
				// Modifico movimiento
				sem_wait(&sem_mov);
				mov.estado = NO_TERMINO;					// No hay ganador todavía
				mov.accion = INVALIDO;				// Si tiene que repetir, por inválida
				mov.posicion = ult_mov;
				sem_post(&sem_mov);
				
				printf("Espero movimiento cliente %d\n",turno);
				
				// Pido a thread_socket un envío y espero resultado
				unlock_socket_s(SEND,sock_jugador);
				sem_wait(&nuevo_mov);
				printf("Llego movimiento cliente %d : %d\n",turno,mov.posicion);			
				
				BuscarCoordenadas(tablero, mov.posicion, &fDestino, &cDestino);
				printf("BuscarCoordenadas OK, fd: %d | cd: %d\n",fDestino, cDestino);
			}while(!ColocarFicha(tablero, turno, fDestino, cDestino));
			
			// Confirmo movimiento
			printf("Tengo que confirmar movimiento\n");
			sem_wait(&sem_mov);
			mov.accion = VALIDO;
			unlock_socket_s(CONF,sock_jugador);

			// Guardo movimiento para el otro jugador
			sem_wait(&sem_mov);
			ult_mov = mov.posicion;
			sem_post(&sem_mov);
			++fichas[turno-1];

			ImprimirTablero(tablero);
			sleep(1);
		}
		
		else  												// Proceso desplazamiento de ficha
		{
			printf("A mover ficha...\n");
			do
			{
				do
				{
					// Modifico movimiento
					sem_wait(&sem_mov);
					mov.estado = NO_TERMINO;					// No hay ganador todavía
					mov.accion = MOVER;					// Realizar movimiento de piezas
					sem_post(&sem_mov);
					
					// Pido a thread_socket un envío y espero resultado
					unlock_socket_s(SEND,sock_jugador);
					sem_wait(&nuevo_mov);
					
					printf("Llego movimiento cliente %d : %d\n",turno,mov.posicion);

					BuscarCoordenadas(tablero, mov.posicion, &fOrigen, &cOrigen);
				}while(!EjecutarSeleccion(tablero, turno, fOrigen, cOrigen));
				
				printf("\n\tFicha %c EjecutarJugada\n", turno == FICHA_O? 'O' : 'X');
				
				// Pido a thread_socket un envío y espero resultado
				unlock_socket_s(SEND,sock_jugador);
				sem_wait(&nuevo_mov);
				
				printf("\t%d -> %d\n",mov.posicion,mov.n_posicion);// scanf("%d", &pos);
				BuscarCoordenadas(tablero, mov.n_posicion, &fDestino, &cDestino);
			}while(!EjecutarJugada(tablero, turno, fOrigen, cOrigen, fDestino, cDestino));
			
			// Guardo movimiento para enviarlo al otro cliente
			//mov.n_posicion = mov.posicion;
			
			tablero[fOrigen][cOrigen] = SIN_FICHA;
			tablero[fDestino][cDestino] = turno;
			
			ImprimirTablero(tablero);
			
			//Confirmo movimiento
			sem_wait(&sem_mov);
			mov.accion = MOVER_CONF;
			printf("Movimiento de %d a %d de jugador %d\n",mov.posicion,mov.n_posicion,turno);
			unlock_socket_s(CONF,sock_jugador);
		}

		ganador = HayGanador(tablero, turno);

	}while(ganador != FICHA_O && ganador != FICHA_X);
	
	ImprimirTablero(tablero);
	printf("\n\tFicha ganadora %c", ganador == FICHA_O? 'O' : 'X');
	
	return ganador == FICHA_O;
}

void errorServPartida(const char *e)
{
	// Error Log
	//FILE* arch = fopen("error_log","wt");
	fprintf(stdout,"%d>> (Partida) %s\n",getpid(),e);
	fcloseall();
	exit(1);
}

void iniciar_semaforos()
{
	// Semáforos
	sem_init(&nuevo_mov,4,0);						// Mutex para inicio partida
	sem_init(&sem_mov,4,1);							// Mutex para mov
	
	// Semáforos sincronización threads
	sem_init(&mutex_socket_s,3,0);
	sem_init(&socket_disp,3,0);
}
